package cn.iocoder.yudao.module.system.service.mail;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.module.system.api.mail.dto.MailSendCreateReqDTO;
import cn.iocoder.yudao.module.system.controller.app.mail.vo.SendMailVo;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO;
import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
import cn.iocoder.yudao.module.system.service.member.MemberService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.Validate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 邮箱发送 Service 实现类
 *
 * @author wangjingyi
 * @since 2022-03-21
 */
@Service
@Validated
@Slf4j
public class MailSendServiceImpl implements MailSendService {

    @Resource
    private AdminUserService adminUserService;
    @Resource
    private MemberService memberService;

    @Resource
    private MailAccountService mailAccountService;
    @Resource
    private MailTemplateService mailTemplateService;

    @Resource
    private MailLogService mailLogService;
    @Resource
    private MailProducer mailProducer;
    private static final String TEMPLATE_CODE_MEMBER = "regist001";

    private static final String FORGOT_PASSWORD = "forgot_password" ;
    @Resource
    private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;

    @Override
    public Long sendSingleMailToAdmin(String mail, Long userId,
                                      String templateCode, Map<String, Object> templateParams) {
        // 如果 mail 为空，则加载用户编号对应的邮箱
        if (StrUtil.isEmpty(mail)) {
            AdminUserDO user = adminUserService.getUser(userId);
            if (user != null) {
                mail = user.getEmail();
            }
        }
        // 执行发送
        return sendSingleMail(mail, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);
    }

    @Override
    public Long sendSingleMailToMember(String mail, Long userId,
                                       String templateCode, Map<String, Object> templateParams) {
        // 如果 mail 为空，则加载用户编号对应的邮箱
        if (StrUtil.isEmpty(mail)) {
            mail = memberService.getMemberUserEmail(userId);
        }
        // 执行发送
        return sendSingleMail(mail, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);
    }

    @Override
    public Long sendSingleMail(String mail, Long userId, Integer userType,
                               String templateCode, Map<String, Object> templateParams) {
        // 校验邮箱模版是否合法
        MailTemplateDO template = validateMailTemplate(templateCode);
        // 校验邮箱账号是否合法
        MailAccountDO account = validateMailAccount(template.getAccountId());

        // 校验邮箱是否存在
        mail = validateMail(mail);
        validateTemplateParams(template, templateParams);

        // 创建发送日志。如果模板被禁用，则不发送短信，只记录日志
        Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus());
        String title = mailTemplateService.formatMailTemplateContent(template.getTitle(), templateParams);
        String content = mailTemplateService.formatMailTemplateContent(template.getContent(), templateParams);
        Long sendLogId = mailLogService.createMailLog(userId, userType, mail,
                account, template, content, templateParams, isSend);
        // 发送 MQ 消息，异步执行发送短信
        if (isSend) {
            mailProducer.sendMailSendMessage(sendLogId, mail, account.getId(),
                    template.getNickname(), title, content);
        }
        return sendLogId;
    }

    @Override
    public void doSendMail(MailSendMessage message) {
        // 1. 创建发送账号
        MailAccountDO account = validateMailAccount(message.getAccountId());
        MailAccount mailAccount  = buildMailAccount(account, message.getNickname());
        // 2. 发送邮件
        try {
            String messageId = MailUtil.send(mailAccount, message.getMail(),
                    message.getTitle(), message.getContent(), true);
            // 3. 更新结果（成功）
            mailLogService.updateMailSendResult(message.getLogId(), messageId, null);
        } catch (Exception e) {
            // 3. 更新结果（异常）
            mailLogService.updateMailSendResult(message.getLogId(), null, e);
        }
    }

    /**
     * 发送有邮箱验证码
     *
     * @param mail
     * @return
     */
    @Override
    public Long sendSingleVerification(String mail) {
        MailSendCreateReqDTO reqDTO =new MailSendCreateReqDTO();
        reqDTO.setMail(mail);
        String verificationCode = generateVerificationCode();
//        this.setVerificationCodeValidityPeriod(mail,verificationCode ,3);
        oauth2AccessTokenRedisDAO.setVerificationCodeValidityPeriod(mail,verificationCode ,300,RedisKeyConstants.REDIS_KEY_EMAIL+mail);
        reqDTO.setTemplateCode(TEMPLATE_CODE_MEMBER);

        // 校验邮箱模版是否合法
        MailTemplateDO template = validateMailTemplate(reqDTO.getTemplateCode());
        // 校验邮箱账号是否合法
        MailAccountDO account = validateMailAccount(template.getAccountId());

        // 校验邮箱是否存在
         validateMail(reqDTO.getMail());
        Map<String, Object> templateParams = MapUtil.of("code", verificationCode);
        validateTemplateParams(template, templateParams);

        // 创建发送日志。如果模板被禁用，则不发送短信，只记录日志
        Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus());
        String title = mailTemplateService.formatMailTemplateContent(template.getTitle(), templateParams);
        String content = "你的注册验证码为:"+verificationCode;
        if (isSend) {
            mailProducer.sendMailSendMessage(null, reqDTO.getMail(), account.getId(),
                    template.getNickname(), title, content);
        }
        return null;
    }

    /**
     * 发送单条邮件给所有用户 APP 的用户
     *
     * @param mail           邮箱
     * @param templateCode   邮件模版编码
     * @param templateParams 邮件模版参数
     * @return 发送日志编号
     */
    @Override
    public Long sendSingleAllMailToMember(String mail, String templateCode, Map<String, Object> templateParams) {
        //定义线程池  查询用户
        Long count = memberService.count();
        //分页查询
        if(count<200){
            List<String> emails = memberService.selectListEmail(1, Math.toIntExact(count));
            //发送短信
            emails.forEach(email -> sendSingleMailToMember(email,null,templateCode,templateParams));
        }else {
            long l = count / 200;
            long l1 = count % 200;
            if (l1!=0){
                l = l+1;
            }
            for (int i = 0; i < l; i++) {
                List<String> emails = memberService.selectListEmail(i+1, 200);
                emails.forEach(email -> sendSingleMailToMember(email,null,templateCode,templateParams));
            }
        }
        return null;
    }

    @Override
    public Long sendMailCode(String email,String mobile) {
        MailSendCreateReqDTO reqDTO =new MailSendCreateReqDTO();
        reqDTO.setMail(email);
        String verificationCode = generateVerificationCode();
        //查寻用户 ，通过手机号

//        this.setVerificationCodeValidityPeriod(mail,verificationCode ,3);
        oauth2AccessTokenRedisDAO.setVerificationCodeValidityPeriod(email,verificationCode ,300,RedisKeyConstants.REDIS_KEY_EMAIL+email+mobile);
        reqDTO.setTemplateCode(FORGOT_PASSWORD);

        // 校验邮箱模版是否合法
        MailTemplateDO template = validateMailTemplate(reqDTO.getTemplateCode());
        // 校验邮箱账号是否合法
        MailAccountDO account = validateMailAccount(template.getAccountId());

        // 校验邮箱是否存在
        validateMail(reqDTO.getMail());
        Map<String, Object> templateParams = MapUtil.of("code", verificationCode);
        validateTemplateParams(template, templateParams);

        // 创建发送日志。如果模板被禁用，则不发送短信，只记录日志
        Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus());
        String title = mailTemplateService.formatMailTemplateContent(template.getTitle(), templateParams);
        String content = "你的验证码为:"+verificationCode;
        if (isSend) {
            mailProducer.sendMailSendMessage(null, reqDTO.getMail(), account.getId(),
                    template.getNickname(), title, content);
        }
        return null;
    }


    /**
     * 验证码
     * @return
     */
    public String generateVerificationCode() {
        //验证码长度
        int length = 6;
        double random = Math.random();
        return String.format("%0" + length + "d", new BigDecimal(random).multiply(BigDecimal.valueOf(Math.pow(10, length))).setScale(0, RoundingMode.HALF_UP).longValue());
    }
    private MailAccount buildMailAccount(MailAccountDO account, String nickname) {
        String from = StrUtil.isNotEmpty(nickname) ? nickname + " <" + account.getMail() + ">" : account.getMail();
        return new MailAccount().setFrom(from).setAuth(true)
                .setUser(account.getUsername()).setPass(account.getPassword())
                .setHost(account.getHost()).setPort(account.getPort())
                .setSslEnable(account.getSslEnable()).setStarttlsEnable(account.getStarttlsEnable());
    }

    @VisibleForTesting
    MailTemplateDO validateMailTemplate(String templateCode) {
        // 获得邮件模板。考虑到效率，从缓存中获取
        MailTemplateDO template = mailTemplateService.getMailTemplateByCodeFromCache(templateCode);
        // 邮件模板不存在
        if (template == null) {
            throw exception(MAIL_TEMPLATE_NOT_EXISTS);
        }
        return template;
    }

    @VisibleForTesting
    MailAccountDO validateMailAccount(Long accountId) {
        // 获得邮箱账号。考虑到效率，从缓存中获取
        MailAccountDO account = mailAccountService.getMailAccountFromCache(accountId);
        // 邮箱账号不存在
        if (account == null) {
            throw exception(MAIL_ACCOUNT_NOT_EXISTS);
        }
        return account;
    }

    @VisibleForTesting
    String validateMail(String mail) {
        if (StrUtil.isEmpty(mail)) {
            throw exception(MAIL_SEND_MAIL_NOT_EXISTS);
        }
        return mail;
    }

    /**
     * 校验邮件参数是否确实
     *
     * @param template 邮箱模板
     * @param templateParams 参数列表
     */
    @VisibleForTesting
    void validateTemplateParams(MailTemplateDO template, Map<String, Object> templateParams) {
        template.getParams().forEach(key -> {
            Object value = templateParams.get(key);
            if (value == null) {
                throw exception(MAIL_SEND_TEMPLATE_PARAM_MISS, key);
            }
        });
    }

}
